/*
 * @Author: 黄威
 * @Date: 2024-04-15 13:18:47
 * @LastEditors: 黄威
 * @LastEditTime: 2024-06-06 10:27:24
 * @Description: json序列胡相关方法
 */

interface TreeNode {
  children?: TreeNode[];
  [key: string]: any;
}

export function flatJSONTree<T extends TreeNode>(
  data: T[],
  fullPath = false,
  pathKey = 'path',
  childrenKey = 'children',
) {
  const retArr: T[] = [];

  const line = [...data];

  while (line.length) {
    const item = line.shift()!;
    retArr.push(item);

    if (item && item[childrenKey]) {
      line.push(
        ...item[childrenKey].map((child) => {
          if (fullPath) child[pathKey] = `${item[pathKey]}/${child[pathKey]}`;
          return child;
        }),
      );
    }
  }

  return retArr;
}

export function flatTreeData<T extends TreeNode>(data: T[], idKey = 'id', childrenKey = 'children') {
  const mappers: Record<string, T> = {};

  const flatten = (nodes) => {
    nodes.forEach((node) => {
      mappers[node[idKey]] = node;

      if (node[childrenKey]) {
        flatten(node[childrenKey]);
      }
    });
  };
  flatten(data);

  return mappers;
}

export function deleteNodeById<T extends TreeNode>(
  data: T[],
  id: string | number,
  idKey = 'id',
  childrenKey = 'children',
) {
  const ret = data;

  const loop = (innerData: T[]) => {
    for (let i = 0; i < innerData.length; i++) {
      const item = innerData[i];

      if (item[idKey] === id) {
        innerData.splice(i, 1);
        return;
      }

      if (item[childrenKey] && item[childrenKey].length) loop(item[childrenKey]);
    }
  };

  loop(ret);

  return ret;
}

export function mixin<T extends TreeNode>(
  source: T[],
  object: T[],
  equalChecker: (sourceNode: T, data: T) => boolean,
  childrenKey = 'children',
) {
  if (!source) return object;
  if (!object) return source;

  const ret = source;

  object.forEach((data) => {
    const sourceNode = ret.find((item) => equalChecker(item, data));

    if (sourceNode) {
      // @ts-ignore mixin
      sourceNode[childrenKey] = mixin(sourceNode[childrenKey], data[childrenKey], equalChecker, childrenKey);
    } else {
      ret.push(data);
    }
  });

  return ret;
}

export function jsonMap<T extends TreeNode, K extends TreeNode>(
  source: T[],
  mapperFunc: (item: T) => K & { children?: K[] },
  filterFunc: (item: T) => boolean = () => true,
  filterFunc2: (item: T) => boolean = () => true,
) {
  return source
    .filter((item) => filterFunc(item))
    .map((item) => {
      const newItem = mapperFunc(item);

      if (filterFunc2(item) && item.children) {
        newItem.children = jsonMap(item.children, mapperFunc, filterFunc, filterFunc2);
      }

      return newItem;
    });
}

export function sortJSONTree<T extends TreeNode>(data: T[], comparer: (a: T, b: T) => number) {
  const ret = data.slice();
  const loop = (nodes: T[]) => {
    nodes.sort(comparer);

    nodes.forEach((node) => {
      if (node.children) loop(node.children as any);
    });
  };
  loop(ret);
  return ret;
}
